This post includes notes that I took from watching a presentation given by Eduardo Ariño de la, the Chief Data Scientist at Domino Data Lab. In his presentation, he introduces 23 visualizations and the appropriate scenarios to use them. You can find the presentation here, Video- 23 Visualizations and when to use them. My goal is to familiarize myself with these plots by writing the code using mainly ggplot and datasets that I am familiar with.

Load Libraries

library(tidyverse)
library(lubridate)
library(readxl)
theme_set(theme_classic())
options(scipen=999)

Loading Global Superstore Dataset

global = read_excel("global_superstore.xls", sheet = 1, col_names = T)

Data Munging

# rename variables
global = rename(global, row_id = `Row ID`, order_id = `Order ID`, order_date = `Order Date`, ship_date = `Ship Date`, ship_mode = `Ship Mode`, customer_id = `Customer ID`, customer_name = `Customer Name`, segment = Segment, city = City, state = State, country = Country, postal_code = `Postal Code`, market = Market, region = Region, product_id = `Product ID`, category = Category, sub_category = `Sub-Category`, product_name = `Product Name`, sales = Sales, quantity = Quantity, discount = `Discount`, profit = `Profit`, shipping_cost = `Shipping Cost`, order_priority = `Order Priority`)

Adding new date columns based on weekday, day, month and year.

global = global %>% 
  mutate(weekday = wday(order_date, label = T),
        day = day(order_date),
        month = month(order_date, label = T),
        year = year(order_date))
global

Deviation

Deviation can be used to emphasize variation from a fixed reference point such as 0, average or any target.

diverging_profit =
global %>%
  mutate(gain_loss = ifelse(profit < 0, 'loss', 'gain')) %>%
  select(order_date, region, profit, gain_loss) %>%
  arrange(profit)
  ggplot(diverging_profit, aes(x = region, y = profit, label = profit)) +
  geom_bar(stat='identity', aes(fill=gain_loss), width=.8) + 
  labs(title = "Diverging Bar", subtitle = "Profit loss or gain from a fixed reference point of 0")+
  coord_flip()+
  scale_fill_manual(values = c("gain"="#1F77B4", "loss"="#FF7F0E"))

ggplot(diverging_profit, aes(x=region, y=profit, label=profit)) + 
  geom_point(stat='identity', aes(colour=gain_loss), size=2, alpha = 0.6) +  
  labs(title = "Diverging Dot Plot", 
       subtitle = "Profit loss or gain from a fixed reference point of 0") + 
  coord_flip() + scale_colour_manual(values = c("gain"="#1F77B4", "loss"="#FF7F0E"))

filter_2012 = global %>% 
  mutate(gain_loss = ifelse(profit > 0, 'gain', 'loss')) %>%
  filter(order_date < "2011-06-01")
  
ggplot(filter_2012, aes(order_date, profit, fill = gain_loss )) +
  geom_area() + labs(title = "Diverging Area Chart", subtitle = "Profit loss or gain from a fixed reference point of 0") + scale_fill_manual(values = c("gain"="#1F77B4", "loss"="#FF7F0E"))

Correlation

Correlation shows the relationship between two or more variables.

ggplot(global, aes(x = sales, y = profit)) + 
  geom_point(aes(col = segment), alpha = 0.6) +
  geom_smooth(method = "loess", se = F) + 
  labs(title = "Scatterplot with Smoothing Line Based on LOESS", subtitle = "Sales vs Profit") +
  scale_colour_manual(values = c("#1F77B4","#FF7F0E", "#2CA02C"))

Ranking

Ranking is useful where ordered list is important.

ranked_region = 
  global %>% 
  select(region, sales)%>%
  group_by(region) %>%
  summarise(sum = sum(sales)) %>%
  mutate(region = factor(region, levels = region[order(sum, decreasing = TRUE)]))
  
ggplot(ranked_region, aes(x = region, y = sum)) +
  geom_bar(stat = "identity", width = .8, fill = "#1F77B4")+ 
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) +
  labs(title = "Ranked Bar Chart", subtitle = "Sales by Region") 

NA
ggplot(ranked_region, aes(x = region, y = sum)) +
  geom_point(size = 3, col = "#1F77B4", alpha = 0.9)+
  geom_segment(aes(x = region, 
               xend = region,
               y = min(sum),
               yend = max(sum)),
              linetype = "dashed",
              size = 0.1) + 
  labs(title = "Dot Plot Ranking Bar", subtitle = "Sales vs Region") +
  coord_flip()

Distribution

Distribution plots show how often values of a variable occur.

ggplot(mpg, aes(x = hwy)) + 
  geom_histogram(bins = 7,col = "black", fill = "#1F77B4") +
  labs(title = "Histogram", subtitle = "Count of Highway Miles") 

Data munging

mtcars$cyl = as.factor(mtcars$cyl)
mtcars
ggplot(mtcars, aes(wt)) + 
  geom_density(aes(fill = factor(cyl)), alpha = 0.7) + xlim(0,6)+
  labs(title="Density plot", 
         subtitle="Weight (1000 lbs) per Cylinder",
         caption="Source: mtcars",
         x="Weight",
         fill="Cylinders") + 
  scale_fill_manual(values = c("#1F77B4", "#2CA02C","#FF7F0E"))

ggplot(mtcars, aes(cyl, qsec, group = cyl)) +
  geom_boxplot(varwidth = T, fill = "#1F77B4") +
  labs(title="Boxplot", 
         subtitle="1/4 mile time per Cylinder",
         caption="Source: mtcars",
         x="Cylinders")

Composition

Compositions graphs show how a single entity can be broken down into its components elements.

ggplot(global, aes(sub_category)) +
  geom_bar(aes(fill = category)) +
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) +
   labs(title="Composition", 
         subtitle="Counts of Sub-Category - Grouped by Category",
         caption="Source: Superstore",
         x="Sub-Category",
         fill = "Category" ) +
  scale_fill_manual(values = c("#1F77B4","#FF7F0E", "#2CA02C"))

NA
global

Stacked Column

ggplot(global, aes(market)) +
  geom_bar(aes(fill = category)) + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) +
  labs(title="Category Stacked - Bar Chart", 
       subtitle="Markets - Grouped by Category", 
       x = "Market",
       fill = "Category",
       caption="Source: Superstore") +
  scale_fill_manual(values = c("#1F77B4","#FF7F0E", "#2CA02C"))

global_treemap = global %>%
  filter(year == 2012, month == 'Jan') %>%
  mutate(country = as.factor(country),
          region = as.factor(region),
         market = as.factor(market))
library(treemapify)
treeMapCoordinates <- treemapify(
  global_treemap,
  area = "sales",
  fill = "profit",
  group = "market")
treeMapPlot <- ggplotify(treeMapCoordinates)
print(treeMapPlot)

Change

Change gives emphasis to changing trends.

year_2012 = 
  global %>% 
  filter(year == 2012)
  ggplot(year_2012, aes(order_date, sales)) + 
    geom_line(col = "#1F77B4") + 
    facet_wrap(~segment, nrow = 3) +
    labs(title="Change Trends", 
       subtitle="Sales by segment", 
       x = "Date",
       caption="Source: Superstore")

Heatmap - with heatmaps we can spot variations of a metric.

library(superheat)
superheat(mtcars_matrix,
          left.label.size = 0.3,
          left.label.text.size = 3,
          legend.text.size = 12,
          padding = .1)


Video - 23 Visualizations and when to use them

LS0tCnRpdGxlOiAnVmlzdWFsaXphdGlvbjogV2hlbiB0byB1c2UgdGhlbSEnCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKVGhpcyBwb3N0IGluY2x1ZGVzIG5vdGVzIHRoYXQgSSB0b29rIGZyb20gd2F0Y2hpbmcgYSBwcmVzZW50YXRpb24gZ2l2ZW4gYnkgRWR1YXJkbyBBcmnDsW8gZGUgbGEsIHRoZSBDaGllZiBEYXRhIFNjaWVudGlzdCBhdCBEb21pbm8gRGF0YSBMYWIuICBJbiBoaXMgcHJlc2VudGF0aW9uLCBoZSBpbnRyb2R1Y2VzIDIzIHZpc3VhbGl6YXRpb25zIGFuZCB0aGUgYXBwcm9wcmlhdGUgc2NlbmFyaW9zIHRvIHVzZSB0aGVtLgpZb3UgY2FuIGZpbmQgdGhlIHByZXNlbnRhdGlvbiBoZXJlLCBbVmlkZW8tIDIzIFZpc3VhbGl6YXRpb25zIGFuZCB3aGVuIHRvIHVzZSB0aGVtXShodHRwczovL2Jsb2cuZG9taW5vZGF0YWxhYi5jb20vdmlkZW8tMjMtdmlzdWFsaXphdGlvbnMtdXNlLykuIE15IGdvYWwgaXMgdG8gZmFtaWxpYXJpemUgbXlzZWxmIHdpdGggdGhlc2UgcGxvdHMgYnkgd3JpdGluZyB0aGUgY29kZSB1c2luZyBtYWlubHkgZ2dwbG90IGFuZCBkYXRhc2V0cyB0aGF0IEkgYW0gZmFtaWxpYXIgd2l0aC4KCiMjI0xvYWQgTGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocmVhZHhsKQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpvcHRpb25zKHNjaXBlbj05OTkpCmBgYAojIyMjTG9hZGluZyBHbG9iYWwgU3VwZXJzdG9yZSBEYXRhc2V0CmBgYHtyfQpnbG9iYWwgPSByZWFkX2V4Y2VsKCJnbG9iYWxfc3VwZXJzdG9yZS54bHMiLCBzaGVldCA9IDEsIGNvbF9uYW1lcyA9IFQpCmBgYAoKIyMjI0RhdGEgTXVuZ2luZwpgYGB7cn0KIyByZW5hbWUgdmFyaWFibGVzCmdsb2JhbCA9IHJlbmFtZShnbG9iYWwsIHJvd19pZCA9IGBSb3cgSURgLCBvcmRlcl9pZCA9IGBPcmRlciBJRGAsIG9yZGVyX2RhdGUgPSBgT3JkZXIgRGF0ZWAsIHNoaXBfZGF0ZSA9IGBTaGlwIERhdGVgLCBzaGlwX21vZGUgPSBgU2hpcCBNb2RlYCwgY3VzdG9tZXJfaWQgPSBgQ3VzdG9tZXIgSURgLCBjdXN0b21lcl9uYW1lID0gYEN1c3RvbWVyIE5hbWVgLCBzZWdtZW50ID0gU2VnbWVudCwgY2l0eSA9IENpdHksIHN0YXRlID0gU3RhdGUsIGNvdW50cnkgPSBDb3VudHJ5LCBwb3N0YWxfY29kZSA9IGBQb3N0YWwgQ29kZWAsIG1hcmtldCA9IE1hcmtldCwgcmVnaW9uID0gUmVnaW9uLCBwcm9kdWN0X2lkID0gYFByb2R1Y3QgSURgLCBjYXRlZ29yeSA9IENhdGVnb3J5LCBzdWJfY2F0ZWdvcnkgPSBgU3ViLUNhdGVnb3J5YCwgcHJvZHVjdF9uYW1lID0gYFByb2R1Y3QgTmFtZWAsIHNhbGVzID0gU2FsZXMsIHF1YW50aXR5ID0gUXVhbnRpdHksIGRpc2NvdW50ID0gYERpc2NvdW50YCwgcHJvZml0ID0gYFByb2ZpdGAsIHNoaXBwaW5nX2Nvc3QgPSBgU2hpcHBpbmcgQ29zdGAsIG9yZGVyX3ByaW9yaXR5ID0gYE9yZGVyIFByaW9yaXR5YCkKYGBgCiMjIyMgQWRkaW5nIG5ldyBkYXRlIGNvbHVtbnMgYmFzZWQgb24gd2Vla2RheSwgZGF5LCBtb250aCBhbmQgeWVhci4KYGBge3J9Cmdsb2JhbCA9IGdsb2JhbCAlPiUgCiAgbXV0YXRlKHdlZWtkYXkgPSB3ZGF5KG9yZGVyX2RhdGUsIGxhYmVsID0gVCksCiAgICAgICAgZGF5ID0gZGF5KG9yZGVyX2RhdGUpLAogICAgICAgIG1vbnRoID0gbW9udGgob3JkZXJfZGF0ZSwgbGFiZWwgPSBUKSwKICAgICAgICB5ZWFyID0geWVhcihvcmRlcl9kYXRlKSkKYGBgCgpgYGB7cn0KZ2xvYmFsCmBgYAoKCiMjI0RldmlhdGlvbiAKIyMjI0RldmlhdGlvbiBjYW4gYmUgdXNlZCB0byBlbXBoYXNpemUgdmFyaWF0aW9uIGZyb20gYSBmaXhlZCByZWZlcmVuY2UgcG9pbnQgc3VjaCBhcyAwLCBhdmVyYWdlIG9yIGFueSB0YXJnZXQuIApgYGB7cn0KZGl2ZXJnaW5nX3Byb2ZpdCA9Cmdsb2JhbCAlPiUKICBtdXRhdGUoZ2Fpbl9sb3NzID0gaWZlbHNlKHByb2ZpdCA8IDAsICdsb3NzJywgJ2dhaW4nKSkgJT4lCiAgc2VsZWN0KG9yZGVyX2RhdGUsIHJlZ2lvbiwgcHJvZml0LCBnYWluX2xvc3MpICU+JQogIGFycmFuZ2UocHJvZml0KQoKICBnZ3Bsb3QoZGl2ZXJnaW5nX3Byb2ZpdCwgYWVzKHggPSByZWdpb24sIHkgPSBwcm9maXQsIGxhYmVsID0gcHJvZml0KSkgKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgYWVzKGZpbGw9Z2Fpbl9sb3NzKSwgd2lkdGg9LjgpICsgCiAgbGFicyh0aXRsZSA9ICJEaXZlcmdpbmcgQmFyIiwgc3VidGl0bGUgPSAiUHJvZml0IGxvc3Mgb3IgZ2FpbiBmcm9tIGEgZml4ZWQgcmVmZXJlbmNlIHBvaW50IG9mIDAiKSsKICBjb29yZF9mbGlwKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZ2FpbiI9IiMxRjc3QjQiLCAibG9zcyI9IiNGRjdGMEUiKSkKCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGl2ZXJnaW5nX3Byb2ZpdCwgYWVzKHg9cmVnaW9uLCB5PXByb2ZpdCwgbGFiZWw9cHJvZml0KSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9J2lkZW50aXR5JywgYWVzKGNvbG91cj1nYWluX2xvc3MpLCBzaXplPTIsIGFscGhhID0gMC42KSArICAKICBsYWJzKHRpdGxlID0gIkRpdmVyZ2luZyBEb3QgUGxvdCIsIAogICAgICAgc3VidGl0bGUgPSAiUHJvZml0IGxvc3Mgb3IgZ2FpbiBmcm9tIGEgZml4ZWQgcmVmZXJlbmNlIHBvaW50IG9mIDAiKSArIAogIGNvb3JkX2ZsaXAoKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiZ2FpbiI9IiMxRjc3QjQiLCAibG9zcyI9IiNGRjdGMEUiKSkKCmBgYAoKCmBgYHtyfQpmaWx0ZXJfMjAxMiA9IGdsb2JhbCAlPiUgCiAgbXV0YXRlKGdhaW5fbG9zcyA9IGlmZWxzZShwcm9maXQgPiAwLCAnZ2FpbicsICdsb3NzJykpICU+JQogIGZpbHRlcihvcmRlcl9kYXRlIDwgIjIwMTEtMDYtMDEiKQogIApnZ3Bsb3QoZmlsdGVyXzIwMTIsIGFlcyhvcmRlcl9kYXRlLCBwcm9maXQsIGZpbGwgPSBnYWluX2xvc3MgKSkgKwogIGdlb21fYXJlYSgpICsgbGFicyh0aXRsZSA9ICJEaXZlcmdpbmcgQXJlYSBDaGFydCIsIHN1YnRpdGxlID0gIlByb2ZpdCBsb3NzIG9yIGdhaW4gZnJvbSBhIGZpeGVkIHJlZmVyZW5jZSBwb2ludCBvZiAwIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJnYWluIj0iIzFGNzdCNCIsICJsb3NzIj0iI0ZGN0YwRSIpKQpgYGAKCgojIyNDb3JyZWxhdGlvbgojIyMjQ29ycmVsYXRpb24gc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBvciBtb3JlIHZhcmlhYmxlcy4KYGBge3J9CmdncGxvdChnbG9iYWwsIGFlcyh4ID0gc2FsZXMsIHkgPSBwcm9maXQpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbCA9IHNlZ21lbnQpLCBhbHBoYSA9IDAuNikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRikgKyAKICBsYWJzKHRpdGxlID0gIlNjYXR0ZXJwbG90IHdpdGggU21vb3RoaW5nIExpbmUgQmFzZWQgb24gTE9FU1MiLCBzdWJ0aXRsZSA9ICJTYWxlcyB2cyBQcm9maXQiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjMUY3N0I0IiwiI0ZGN0YwRSIsICIjMkNBMDJDIikpCgpgYGAKCgojIyMjIExvYWRpbmcgZnVlbCBlY29ub215IGRhdGEgZnJvbSAxOTk5IGFuZCAyMDA4IGZvciAzOCBwb3B1bGFyIG1vZGVscyBvZiBjYXIKYGBge3J9Cm1wZwpgYGAKCgpgYGB7cn0KIGdncGxvdChtcGcsIGFlcyh4ID0gaHd5LCB5ID0gZGlzcGwpKSArCiAgZ2VvbV9wb2ludChjb2xvdXIgPSAiI0ZGN0YwRSIpKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEYpICsgCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCB3aXRoIFNtb290aGluZyBMaW5lIEJhc2VkIG9uIExPRVNTIiwgc3VidGl0bGUgPSAiRW5naW5lIERpc3BsYWNlbWVudCB2cyBIaWdod2F5IE1pbGVzIFBlciBHYWxsb24iKQpgYGAKCgojIyMgUmFua2luZwojIyMjUmFua2luZyBpcyB1c2VmdWwgd2hlcmUgb3JkZXJlZCBsaXN0IGlzIGltcG9ydGFudC4KYGBge3J9CnJhbmtlZF9yZWdpb24gPSAKICBnbG9iYWwgJT4lIAogIHNlbGVjdChyZWdpb24sIHNhbGVzKSU+JQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShzYWxlcykpICU+JQogIG11dGF0ZShyZWdpb24gPSBmYWN0b3IocmVnaW9uLCBsZXZlbHMgPSByZWdpb25bb3JkZXIoc3VtLCBkZWNyZWFzaW5nID0gVFJVRSldKSkKICAKZ2dwbG90KHJhbmtlZF9yZWdpb24sIGFlcyh4ID0gcmVnaW9uLCB5ID0gc3VtKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IC44LCBmaWxsID0gIiMxRjc3QjQiKSsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpICsKICBsYWJzKHRpdGxlID0gIlJhbmtlZCBCYXIgQ2hhcnQiLCBzdWJ0aXRsZSA9ICJTYWxlcyBieSBSZWdpb24iKSAKICAKYGBgCgoKYGBge3J9CmdncGxvdChyYW5rZWRfcmVnaW9uLCBhZXMoeCA9IHJlZ2lvbiwgeSA9IHN1bSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBjb2wgPSAiIzFGNzdCNCIsIGFscGhhID0gMC45KSsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSByZWdpb24sIAogICAgICAgICAgICAgICB4ZW5kID0gcmVnaW9uLAogICAgICAgICAgICAgICB5ID0gbWluKHN1bSksCiAgICAgICAgICAgICAgIHllbmQgPSBtYXgoc3VtKSksCiAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICBzaXplID0gMC4xKSArIAogIGxhYnModGl0bGUgPSAiRG90IFBsb3QgUmFua2luZyBCYXIiLCBzdWJ0aXRsZSA9ICJTYWxlcyB2cyBSZWdpb24iKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMjRGlzdHJpYnV0aW9uCkRpc3RyaWJ1dGlvbiBwbG90cyBzaG93IGhvdyBvZnRlbiB2YWx1ZXMgb2YgYSB2YXJpYWJsZSBvY2N1ci4KYGBge3J9CmdncGxvdChtcGcsIGFlcyh4ID0gaHd5KSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNyxjb2wgPSAiYmxhY2siLCBmaWxsID0gIiMxRjc3QjQiKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0iLCBzdWJ0aXRsZSA9ICJDb3VudCBvZiBIaWdod2F5IE1pbGVzIikgCmBgYAoKIyMjIyBEYXRhIG11bmdpbmcKYGBge3J9Cm10Y2FycyRjeWwgPSBhcy5mYWN0b3IobXRjYXJzJGN5bCkKbXRjYXJzCmBgYAoKCmBgYHtyfQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QpKSArIAogIGdlb21fZGVuc2l0eShhZXMoZmlsbCA9IGZhY3RvcihjeWwpKSwgYWxwaGEgPSAwLjcpICsgeGxpbSgwLDYpKwogIGxhYnModGl0bGU9IkRlbnNpdHkgcGxvdCIsIAogICAgICAgICBzdWJ0aXRsZT0iV2VpZ2h0ICgxMDAwIGxicykgcGVyIEN5bGluZGVyIiwKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBtdGNhcnMiLAogICAgICAgICB4PSJXZWlnaHQiLAogICAgICAgICBmaWxsPSJDeWxpbmRlcnMiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxRjc3QjQiLCAiIzJDQTAyQyIsIiNGRjdGMEUiKSkKCmBgYAoKYGBge3J9CmdncGxvdChtdGNhcnMsIGFlcyhjeWwsIHFzZWMsIGdyb3VwID0gY3lsKSkgKwogIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFQsIGZpbGwgPSAiIzFGNzdCNCIpICsKICBsYWJzKHRpdGxlPSJCb3hwbG90IiwgCiAgICAgICAgIHN1YnRpdGxlPSIxLzQgbWlsZSB0aW1lIHBlciBDeWxpbmRlciIsCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogbXRjYXJzIiwKICAgICAgICAgeD0iQ3lsaW5kZXJzIikKYGBgCgoKIyMjQ29tcG9zaXRpb24gCiMjIyMgQ29tcG9zaXRpb25zIGdyYXBocyBzaG93IGhvdyBhIHNpbmdsZSBlbnRpdHkgY2FuIGJlIGJyb2tlbiBkb3duIGludG8gaXRzIGNvbXBvbmVudHMgZWxlbWVudHMuIApgYGB7cn0KZ2dwbG90KGdsb2JhbCwgYWVzKHN1Yl9jYXRlZ29yeSkpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IGNhdGVnb3J5KSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjYpKSArCiAgIGxhYnModGl0bGU9IkNvbXBvc2l0aW9uIiwgCiAgICAgICAgIHN1YnRpdGxlPSJDb3VudHMgb2YgU3ViLUNhdGVnb3J5IC0gR3JvdXBlZCBieSBDYXRlZ29yeSIsCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogU3VwZXJzdG9yZSIsCiAgICAgICAgIHg9IlN1Yi1DYXRlZ29yeSIsCiAgICAgICAgIGZpbGwgPSAiQ2F0ZWdvcnkiICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxRjc3QjQiLCIjRkY3RjBFIiwgIiMyQ0EwMkMiKSkKICAKYGBgCmBgYHtyfQpnbG9iYWwKYGBgCgoKU3RhY2tlZCBDb2x1bW4KYGBge3J9CmdncGxvdChnbG9iYWwsIGFlcyhtYXJrZXQpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBjYXRlZ29yeSkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpICsKICBsYWJzKHRpdGxlPSJDYXRlZ29yeSBTdGFja2VkIC0gQmFyIENoYXJ0IiwgCiAgICAgICBzdWJ0aXRsZT0iTWFya2V0cyAtIEdyb3VwZWQgYnkgQ2F0ZWdvcnkiLCAKICAgICAgIHggPSAiTWFya2V0IiwKICAgICAgIGZpbGwgPSAiQ2F0ZWdvcnkiLAogICAgICAgY2FwdGlvbj0iU291cmNlOiBTdXBlcnN0b3JlIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxRjc3QjQiLCIjRkY3RjBFIiwgIiMyQ0EwMkMiKSkKYGBgCgoKYGBge3J9IApnbG9iYWxfdHJlZW1hcCA9IGdsb2JhbCAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDEyLCBtb250aCA9PSAnSmFuJykgJT4lCiAgbXV0YXRlKGNvdW50cnkgPSBhcy5mYWN0b3IoY291bnRyeSksCiAgICAgICAgICByZWdpb24gPSBhcy5mYWN0b3IocmVnaW9uKSwKICAgICAgICAgbWFya2V0ID0gYXMuZmFjdG9yKG1hcmtldCkpCgpsaWJyYXJ5KHRyZWVtYXBpZnkpCnRyZWVNYXBDb29yZGluYXRlcyA8LSB0cmVlbWFwaWZ5KAogIGdsb2JhbF90cmVlbWFwLAogIGFyZWEgPSAic2FsZXMiLAogIGZpbGwgPSAicHJvZml0IiwKICBncm91cCA9ICJtYXJrZXQiKQp0cmVlTWFwUGxvdCA8LSBnZ3Bsb3RpZnkodHJlZU1hcENvb3JkaW5hdGVzKQpwcmludCh0cmVlTWFwUGxvdCkKYGBgCgoKIyMjIENoYW5nZSAKIyMjI0NoYW5nZSBnaXZlcyBlbXBoYXNpcyB0byBjaGFuZ2luZyB0cmVuZHMuICAKYGBge3J9CnllYXJfMjAxMiA9IAogIGdsb2JhbCAlPiUgCiAgZmlsdGVyKHllYXIgPT0gMjAxMikKICBnZ3Bsb3QoeWVhcl8yMDEyLCBhZXMob3JkZXJfZGF0ZSwgc2FsZXMpKSArIAogICAgZ2VvbV9saW5lKGNvbCA9ICIjMUY3N0I0IikgKyAKICAgIGZhY2V0X3dyYXAofnNlZ21lbnQsIG5yb3cgPSAzKSArCiAgICBsYWJzKHRpdGxlPSJDaGFuZ2UgVHJlbmRzIiwgCiAgICAgICBzdWJ0aXRsZT0iU2FsZXMgYnkgc2VnbWVudCIsIAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIGNhcHRpb249IlNvdXJjZTogU3VwZXJzdG9yZSIpCmBgYAoKIyMjIyBIZWF0bWFwIC0gd2l0aCBoZWF0bWFwcyB3ZSBjYW4gc3BvdCB2YXJpYXRpb25zIG9mIGEgbWV0cmljLgpgYGB7cn0KbGlicmFyeShzdXBlcmhlYXQpCnN1cGVyaGVhdChtdGNhcnNfbWF0cml4LAogICAgICAgICAgbGVmdC5sYWJlbC5zaXplID0gMC4zLAogICAgICAgICAgbGVmdC5sYWJlbC50ZXh0LnNpemUgPSAzLAogICAgICAgICAgbGVnZW5kLnRleHQuc2l6ZSA9IDEyLAogICAgICAgICAgcGFkZGluZyA9IC4xKQpgYGAKCgoqKioKW1ZpZGVvIC0gMjMgVmlzdWFsaXphdGlvbnMgYW5kIHdoZW4gdG8gdXNlIHRoZW1dKGh0dHBzOi8vYmxvZy5kb21pbm9kYXRhbGFiLmNvbS92aWRlby0yMy12aXN1YWxpemF0aW9ucy11c2UvKQ==